package com.carleasoft.mps.core.utils;

import com.carleasoft.mps.core.exceptions.ParamException;
import com.carleasoft.mps.core.result.ErrorResult;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 自定义的Bean工具类
 *
 * @author Alay
 * @date 2021-07-07 13:28
 * @project jmrh
 */
@Slf4j
public class IBeanUtil {
    /**
     * 原型模式(深拷贝)
     *
     * @param source
     * @param <T>    克隆的实体 Bean 必须实现了 Serializable 接口
     * @return
     */
    public static <T extends Serializable> T clone(T source) {
        ByteArrayOutputStream bos = null;
        ByteArrayInputStream bis = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        T target = null;
        try {
            // 系列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);

            // 当前这个对象与对象流形式输出
            oos.writeObject(source);
            // 反系列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);

            // 将输入流读取称为对象
            target = (T) ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return target;
    }


    /**
     * 获取所有字段(不含静态、常量字段)
     *
     * @param clazz
     * @param fieldNames
     */
    public static void allField(Class<?> clazz, List<Field> fieldNames) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            int modifiers = field.getModifiers();
            String modifierStr = Modifier.toString(modifiers);
            // 不包含的修饰符的字段
            if (modifierStr.contains("static") || modifierStr.contains("final")
                    || modifierStr.contains("native") || modifierStr.contains("transient")) {
                continue;
            }
            fieldNames.add(field);
        }
        Class<?> superclass = clazz.getSuperclass();
        if (null != superclass) {
            allField(superclass, fieldNames);
        }
    }

    /**
     * 判断对象是否有效对象(对象的值是否为null)
     *
     * @param bean
     * @param ignoreFields
     * @param <T>
     * @return 对象无效返回 false,有效返回 true
     */
    public static <T> boolean isEffective(T bean, String... ignoreFields) {
        boolean isEffective = false;
        Class<?> clazz = bean.getClass();
        List<Field> fields = new ArrayList<>();
        allField(clazz, fields);
        Iterator<Field> iterator = fields.iterator();
        List<String> ignores = Arrays.asList(ignoreFields);
        try {
            while (iterator.hasNext()) {
                Field field = iterator.next();
                if (ignores.contains(field.getName())) {
                    continue;
                }
                field.setAccessible(true);
                Object value = field.get(bean);
                if (null != value) {
                    isEffective = true;
                    break;
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return isEffective;
    }


    /**
     * 验证对象值的参数（Controller 调用）
     *
     * @param source           实体对象,Map<String,Object>类型，Map 的Key 必须是 String 类型
     * @param verifyFieldNames 验证的字段名
     * @param <T>
     */
    public static <T> void paramVerify(T source, String... verifyFieldNames) {
        List<String> verifyFields = ICollUtil.asList(verifyFieldNames);
        if (source instanceof Map) {
            // Map 类型，Map 的Key 必须是 String 类型
            Map<String, Object> map = (Map<String, Object>) source;
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                if (verifyFields.contains(key)) {
                    Object value = entry.getValue();
                    if (null == value) {
                        log.error("参数字段:>>>> {} <<<< 值为空", key);
                        throw ExceptionUtil.create(ErrorResult.PARAMETER_ERROR);
                    }
                }
                continue;
            }
        } else {
            Class<?> clazz = source.getClass();
            List<Field> fields = new ArrayList<>();
            allField(clazz, fields);
            Iterator<Field> iterator = fields.iterator();
            try {
                while (iterator.hasNext()) {
                    Field field = iterator.next();
                    if (!verifyFields.contains(field.getName())) {
                        continue;
                    }
                    field.setAccessible(true);
                    Object value = field.get(source);
                    if (null == value) {
                        log.error("参数字段:>>>> {} <<<< 值为空", field.getName());
                        throw ExceptionUtil.create(ErrorResult.PARAMETER_ERROR);
                    } else if (value instanceof String) {
                        String valueStr = (String) value;
                        if ("".equals(valueStr)) {
                            log.error("参数字段:>>>> {} <<<< 值为空字符串", field.getName());
                            throw ExceptionUtil.create(ErrorResult.PARAMETER_ERROR);
                        }
                    }
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }




    /**
     * 根据名字获取枚举对象
     *
     * @param clazz
     * @param name
     * @param <E>
     * @return
     */
    public static <E extends Enum> E sourceOf(Class<E> clazz, String name) {
        try {
            Method valuesMethod = clazz.getMethod("values");
            E[] enumInstanceArr = (E[]) valuesMethod.invoke(null);
            for (E enumInstance : enumInstanceArr) {
                if (Objects.equals(enumInstance.name(), name)) {
                    return enumInstance;
                }
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

}
